package edu.kit.aifb.ldstream.learner.tasks;

import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.Map;
import java.util.Set;

import org.openrdf.model.BNode;
import org.openrdf.model.Literal;
import org.openrdf.model.Statement;
import org.openrdf.model.URI;
import org.openrdf.model.Value;
import org.openrdf.repository.RepositoryException;
import org.openrdf.repository.RepositoryResult;
import org.openrdf.rio.ParserConfig;
import org.openrdf.rio.RDFParser.DatatypeHandling;

import edu.kit.aifb.ldstream.learner.LiteralChecker;
import edu.kit.aifb.ldstream.learner.dao.Instance;
import edu.kit.aifb.ldstream.learner.dao.ValueDistributionResult;
import edu.kit.aifb.ldstream.learner.dao.Type;
import edu.kit.aifb.ldstream.stat.util.IDistributionChecker;

public class ValueDistributionTask extends LearnerTask {

	private Map<Type, HashSet<Instance>> classMap;
	private Map<Type, HashMap<URI, ValueDistributionResult>> resultSet;
	private IDistributionChecker distribChecker;


	public ValueDistributionTask(Map<Type, HashSet<Instance>> classMap, 
								Map<Type, HashMap<URI, ValueDistributionResult>> resultSet, 
								IDistributionChecker distributionChecker) {
		this.classMap = classMap;
		this.resultSet = resultSet;
		this.distribChecker = distributionChecker;
	}
	@Override
	public void run() {

		startup();
		setPreserveBNodes();
		collectValuesAndDistribution();
		shutdown();		
	}

	private void collectValuesAndDistribution(){

		Statement s;
		Value obj;
		Literal lit;

		for(Type type : this.classMap.keySet()){
			// only crawl if the class has some instances
			if(!this.classMap.get(type).isEmpty()){
				this.resultSet.put(type, new HashMap<URI, ValueDistributionResult>());
				Map<URI, LinkedList<Double>> valueList = new HashMap<URI, LinkedList<Double>>();
				Map<URI, LinkedList<String>> stringList = new HashMap<URI, LinkedList<String>>();
				for(Instance instance : this.classMap.get(type)){
					RepositoryResult<Statement> result = null;
					try {
						result = con.getStatements(instance.getURI(), null, null, false);
						while(result.hasNext()){
							s = result.next();
							obj = s.getObject();
							if(obj instanceof BNode){
								// crawl
								collectValuesAndDistribution((BNode) obj, valueList, stringList);
							}else if (obj instanceof Literal){
								lit = (Literal) obj;
								if(LiteralChecker.isQuantitativeLiteral(lit.getDatatype())){
									// adding if the predicate is not crawled yet we need a new list to save the values
									if(!valueList.containsKey(s.getPredicate())){
										valueList.put(s.getPredicate(), new LinkedList<Double>());
									}
									valueList.get(s.getPredicate()).add(lit.doubleValue());
									// if its qualitative we save the values as strings 
								}else if(LiteralChecker.isQualitativeLiteral(lit.getDatatype())){
									if(!stringList.containsKey(s.getPredicate())){
										stringList.put(s.getPredicate(), new LinkedList<String>());
									}
									stringList.get(s.getPredicate()).add(lit.stringValue());
								}
							}else{
								/*
								 * 
								 *  TODO!!!!
								 *  
								 */
								if(!stringList.containsKey(s.getPredicate())){
									stringList.put(s.getPredicate(), new LinkedList<String>());
								}
								stringList.get(s.getPredicate()).add(obj.stringValue());
							}
						}
					} catch (RepositoryException e) {
						e.printStackTrace();
					}
				}
				// all instance of that class are crawled no find out the distribution of the values
				checkContinuousDistribution(type, valueList);
				checkDiscreteDistribution(type, stringList);
			}
		}
	}

	private void collectValuesAndDistribution(BNode b, 
			Map<URI, LinkedList<Double>> valueList,
			Map<URI, LinkedList<String>> stringList){
		try {
			RepositoryResult<Statement> result = con.getStatements(b, null, null, false);
			Statement s;
			Literal lit;
			Value obj;

			try{
				while(result.hasNext()){
					s = result.next();
					obj = s.getObject();
					if(obj instanceof BNode){
						collectValuesAndDistribution((BNode) obj, valueList, stringList);
					}else if(obj instanceof Literal){
						lit = (Literal) obj;
						
						if(LiteralChecker.isQuantitativeLiteral(lit.getDatatype())){
							// adding if the predicate is not crawled yet we need a new list to save the values
							if(!valueList.containsKey(s.getPredicate())){
								valueList.put(s.getPredicate(), new LinkedList<Double>());
							}
							valueList.get(s.getPredicate()).add(lit.doubleValue());
							// if its qualitative we save the values as strings 
						}else if(LiteralChecker.isQualitativeLiteral(lit.getDatatype())){
							if(!stringList.containsKey(s.getPredicate())){
								stringList.put(s.getPredicate(), new LinkedList<String>());
							}
							stringList.get(s.getPredicate()).add(lit.stringValue());
						}
					}else{
						/*
						 * 
						 *  
						 *  TODO sollen nicht Literal vorkommen? wie wird Datentyp erkannt?
						 *  
						 *  
						 */
						if(!stringList.containsKey(s.getPredicate())){
							stringList.put(s.getPredicate(), new LinkedList<String>());
						}
						stringList.get(s.getPredicate()).add(obj.stringValue());
					}
				}
			}finally{
				result.close();
			}
		} catch (RepositoryException e) {
			e.printStackTrace();
		}
	}

	private void checkContinuousDistribution(Type t, Map<URI, LinkedList<Double>> valueList){

		for(URI pred : valueList.keySet()){
			// get all the values of the specified predicate
			double[] values = new double[valueList.get(pred).size()];
			int i = 0;
			for(double entry : valueList.get(pred)){
				values[i++] = entry;
			}
			ValueDistributionResult r = new ValueDistributionResult(t.getURI(), pred, this.distribChecker.continuous(values));
			System.out.println(r.toString());
			this.resultSet.get(t).put(pred, r);
		}

	}

	private void checkDiscreteDistribution(Type t, Map<URI, LinkedList<String>> stringList){

		for(URI pred : stringList.keySet()){
			String[] values = new String[stringList.get(pred).size()];
			int i = 0;
			for(String entry : stringList.get(pred)){
				values[i++] = entry;
			}
			ValueDistributionResult r = new ValueDistributionResult(t.getURI(), pred, this.distribChecker.discrete(values));
			System.out.println(r.toString());
			this.resultSet.get(t).put(pred, r);
		}
	}


	private void setPreserveBNodes(){
		this.con.setParserConfig(new ParserConfig(true, true, true, DatatypeHandling.VERIFY));
	}
}




